/* Copyright Thanh-Trung Pham, JAIST, 2013 */
package jp.ac.jaist.input;

import jp.ac.jaist.FailureModelType;
import jp.ac.jaist.FailureModelType.Input;
import jp.ac.jaist.FailureModelType.Input.Output;
import jp.ac.jaist.FailureModelType.Input.SignaledFailure;
import org.apache.commons.collections.map.MultiKeyMap;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @author Thanh-Trung Pham
 */
public class RMPIIAWrapper {

    private static final String RMP_F0 = "F0";
    private static final String RMP_COMMA = ",";
    private MultiKeyMap m_oFailureModelData = new MultiKeyMap();

    private RMPIInputModeler m_oInputModel;


    /**
     * The constructor.
     * @param inputMode the input model.
     * @param areAllZero true to create an IA wrapper with all zero elements, false to create an identify IA wrapper.
     */
    public RMPIIAWrapper(RMPIInputModeler inputMode, boolean areAllZero) {
        if (areAllZero) {
            createAllZeroIAWrapper();
            return;
        }

        m_oInputModel = inputMode;

        createIdentityIAWrapper();
    }

    /**
     * The constructor.
     *
     * @param failureModel a failure model.
     */
    public RMPIIAWrapper(RMPIInputModeler inputMode, FailureModelType failureModel) {
        m_oInputModel = inputMode;

        if (failureModel == null) {
            createIdentityIAWrapper();
            return;
        }

        createPerfectIAWrapper();
        Input[] oInputs = failureModel.getInputArray();
        for (Input oInput : oInputs) {
            String strInputFailures = oInput.getInvolvedFailures();

            Set<String> oSetOfInputFailures = new HashSet<String>();
            if (RMP_F0.contains(strInputFailures)) {
                //oSetOfInputFailures.add(RMP_F0);
            } else {
                String[] oInputFailures = StringUtils.split(strInputFailures, RMP_COMMA);
                oSetOfInputFailures.addAll(Arrays.asList(oInputFailures));
            }

            double dTotalProbability = 0.0;
            SignaledFailure[] oSignaledFailures = oInput.getSignaledFailureArray();
            for (SignaledFailure oSignaledFailure : oSignaledFailures) {
                String strSignaledFailure = oSignaledFailure.getInvolvedFailure();
                double dProbability = oSignaledFailure.getProbability();
                m_oFailureModelData.put(oSetOfInputFailures, new HashSet<String>(Arrays.asList(new String[]{strSignaledFailure})), dProbability);

                dTotalProbability += dProbability;
            }

            Output[] oOutputs = oInput.getOutputArray();
            for (Output oOutput : oOutputs) {
                Set<String> oSetOfOutputFailures = new HashSet<String>();

                String strOutputFailures = oOutput.getInvolvedFailures();
                if (RMP_F0.equals(strOutputFailures)) {
                    //oSetOfOutputFailures.add(RMP_F0);
                } else {
                    String[] oOutputFailures = StringUtils.split(strOutputFailures, RMP_COMMA);
                    oSetOfOutputFailures.addAll(Arrays.asList(oOutputFailures));
                }

                double dProbability = oOutput.getProbability();
                m_oFailureModelData.put(oSetOfInputFailures, oSetOfOutputFailures, dProbability);

                dTotalProbability += dProbability;
            }

            m_oFailureModelData.put(oSetOfInputFailures, RMPIInputModeler.RMPI_F0_AS_SET, 1.0 - dTotalProbability);
        }
    }

    /**
     * Create an identity IA Wrapper.
     */
    private void createIdentityIAWrapper() {
        Set<Set<String>> oAIOS = m_oInputModel.getAIOS();
        for (Set<String> oAIO : oAIOS) {
            m_oFailureModelData.put(oAIO, oAIO, 1.0);
        }
    }

    /**
     * Create a perfect IA Wrapper.
     */
    private void createPerfectIAWrapper() {
        Set<Set<String>> oAIOS = m_oInputModel.getAIOS();
        for (Set<String> oAIO : oAIOS) {
            m_oFailureModelData.put(oAIO, RMPIInputModeler.RMPI_F0_AS_SET, 1.0);
        }
    }

    /**
     * Create an all zero IA Wrapper..
     */
    private void createAllZeroIAWrapper() {
        //by default, do nothing
    }

    /**
     * Get a probability.
     *
     * @param inputFailures  a set of input failures.
     * @param outputFailures a set of output failures.
     * @return a probability.
     */
    public Double getProbability(Set<String> inputFailures, Set<String> outputFailures) {
        Double oProbability = (Double) m_oFailureModelData.get(inputFailures, outputFailures);
        if (oProbability != null) {
            return oProbability;
        }

        return new Double(0);
    }

    /**
     * Set a probability.
     *
     * @param inputFailures  a set of input failures.
     * @param outputFailures a set of output failures.
     * @param probability    the probability to set.
     */
    public void setProbability(Set<String> inputFailures, Set<String> outputFailures, double probability) {
        if (probability != 0) {
            m_oFailureModelData.put(inputFailures, outputFailures, probability);
        } else {
            m_oFailureModelData.remove(inputFailures, outputFailures);
        }
    }

    @Override
    public String toString() {
        StringBuilder oReturnContent = new StringBuilder();

        Set<Set<String>> oAIOS = m_oInputModel.getAIOS();
        Set<Set<String>> oAFS = m_oInputModel.getAFS();

        for (Set<String> oInputSet : oAIOS) {
            oReturnContent.append("Input's involved failures '").append(getListOfFailureTypesAsString(oInputSet)).append("'\n");
            for (Set<String> oSignaledFailures : oAFS) {
                oReturnContent.append("\tSignaled failure's involved failure '").append(getListOfFailureTypesAsString(oSignaledFailures)).append("' probability=").append(getProbability(oInputSet, oSignaledFailures)).append("\n");
                getProbability(oInputSet, oSignaledFailures);
            }

            for (Set<String> oOutputSet : oAIOS) {
                oReturnContent.append("\tOutput's involved failures '").append(getListOfFailureTypesAsString(oOutputSet)).append("' probability=").append(getProbability(oInputSet, oOutputSet)).append("\n");
                getProbability(oInputSet, oOutputSet);
            }
        }

        return oReturnContent.toString();
    }

    /**
     * Get a list of failure types as string.
     *
     * @param setOfFailureTypes a set of failure types.
     * @return the list of failure types as string.
     */
    private String getListOfFailureTypesAsString(Set<String> setOfFailureTypes) {
        String strList = "";
        if (setOfFailureTypes.isEmpty()) {
            return "F0";
        }

        for (String strFailureType : setOfFailureTypes) {
            if (strList.equals("")) {
                strList += strFailureType;
            } else {
                strList += ("," + strFailureType);
            }
        }

        return strList;
    }
}
